package cate.game.framework.advanced;

import cate.common.table.d.GDRank;
import cate.game.GameBody;
import cate.game.rank.h.IRankFuncHandler;
import cate.game.rank.po.Rank;
import cate.game.rank.vo.RankParam;
import cate.game.role.Role;
import com.google.common.collect.Lists;
import easy.java.practice.errorcode.EcResult;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;

public class AdvancedRankHandlerAdapter<T extends Rank> implements IRankFuncHandler<T> {

    public static final Duration TIMEOUT = Duration.ofSeconds(2);

    private final int rankId;
    private final int rankLimit;

    private final Function<GameBody, AdvancedRankHandler<T>> function;
    private GameBody gameBody;

    public AdvancedRankHandlerAdapter(
            Function<GameBody, AdvancedRankHandler<T>> function,
            int rankId) {
        this(function, rankId, GDRank.RANK_LIMIT);
    }

    public AdvancedRankHandlerAdapter(
            Function<GameBody, AdvancedRankHandler<T>> function,
            int rankId,
            int rankLimit) {
        this.function = Objects.requireNonNull(function);
        this.rankId = rankId;
        this.rankLimit = rankLimit;
    }

    public void initialize(GameBody gameBody) {
        this.gameBody = gameBody;
//        Objects.requireNonNull(function.apply(gameBody));
        gameBody.rank.registeHandler(this);
    }

    @Override
    public int getRankId() {
        return this.rankId;
    }

    @Override
    public T getRankOneData(RankParam param) {
        return applyWithRank(
                (rankHandler) ->
                        rankHandler.getRankFlow()
                                .timeout(TIMEOUT)
                                .blockFirst(),
                null);
    }

    static final EcResult<Void> ADMIRE_FAIL = new EcResult<Void>().fail();

    @Override
    public EcResult<Void> admire(RankParam param, String roleUid, int rank) {
        return applyWithRank(
                (rankHandler) ->
                        rankHandler.admire(roleUid, rank),
                ADMIRE_FAIL
        );
    }

    @Override
    public T getData(RankParam param, Role role) {
        return applyWithRank(
                (rankHandler) ->
                        rankHandler.getData(role.getUid()),
                null
        );
    }

    @Override
    public List<T> getRankList(RankParam param) {
        return applyWithRank(
                (rankHandler) ->
                        rankHandler.getRankFlow()
                                .take(this.rankLimit)
                                .timeout(TIMEOUT)
                                .onErrorResume(e -> Mono.empty())
                                .collectList()
                                .blockOptional()
                                .orElse(null),
                Lists.newArrayList()
        );
    }

    private <E> E applyWithRank(Function<AdvancedRankHandler<T>, E> consumer, E defaultValue) {
        E result = consumer.apply(getRankHandler());
        return Objects.nonNull(result)
                ? result
                : defaultValue;
    }

    protected AdvancedRankHandler<T> getRankHandler() {
        return this.function.apply(this.gameBody);
    }
}
